package com.example.filter;
import com.example.utils.Const;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;

/**
 * 跨域配置过滤器，仅处理跨域，添加跨域响应头
 * 该过滤器用于处理跨域资源共享（CORS）相关的请求，通过添加相应的响应头来允许跨域请求。
 */
@Component
@Order(Const.ORDER_CORS)
public class CorsFilter extends HttpFilter {

    // 从配置文件中获取允许的源（origin），即允许哪些域名访问资源
    @Value("${spring.web.cors.origin}")
    String allowedOrigins;

    // 从配置文件中获取是否允许携带凭证（credentials），如cookie等
    @Value("${spring.web.cors.credentials}")
    boolean allowCredentials;

    // 从配置文件中获取允许的请求方法（methods），如GET、POST等
    @Value("${spring.web.cors.methods}")
    String allowedMethods;

    /**
     * 过滤器的核心处理方法，在请求处理链中被调用。
     * 此方法首先添加跨域相关的响应头，然后将请求传递给下一个过滤器或目标资源。
     *
     * @param request  HttpServletRequest对象，包含客户端的请求信息。
     * @param response HttpServletResponse对象，用于设置响应头和响应内容。
     * @param chain    FilterChain对象，用于将请求传递给下一个过滤器或目标资源。
     * @throws IOException  如果在处理请求或响应时发生I/O错误。
     * @throws ServletException 如果在处理请求或响应时发生Servlet相关的异常。
     */
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 添加跨域相关的响应头
        this.addCorsHeader(request, response);
        // 将请求传递给下一个过滤器或目标资源
        chain.doFilter(request, response);
    }

    /**
     * 添加所有跨域相关响应头。
     * 此方法根据配置和请求信息，设置Access-Control-Allow-Origin、Access-Control-Allow-Methods、
     * Access-Control-Allow-Headers和Access-Control-Allow-Credentials等响应头。
     *
     * @param request  HttpServletRequest对象，用于获取请求的来源信息。
     * @param response HttpServletResponse对象，用于添加响应头。
     */
    private void addCorsHeader(HttpServletRequest request, HttpServletResponse response) {
        // 设置允许的源（Origin）响应头，通过resolveOrigin方法解析得到具体的值
        response.addHeader("Access-Control-Allow-Origin", this.resolveOrigin(request));
        // 设置允许的请求方法（Methods）响应头，通过resolveMethod方法解析得到具体的值
        response.addHeader("Access-Control-Allow-Methods", this.resolveMethod());
        // 设置允许的请求头（Headers）响应头，这里固定设置为"Authorization, Content-Type"
        response.addHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
        // 如果配置中允许携带凭证，则设置Access-Control-Allow-Credentials响应头为"true"
        if (allowCredentials) {
            response.addHeader("Access-Control-Allow-Credentials", "true");
        }
    }

    /**
     * 解析配置文件中的请求方法。
     * 如果配置中的方法为"*"，则返回所有常见的请求方法；否则返回配置中指定的方法。
     *
     * @return 解析得到的请求头值，即允许的请求方法列表。
     */
    private String resolveMethod() {
        return allowedMethods.equals("*")? "GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, PATCH" : allowedMethods;
    }

    /**
     * 解析配置文件中的请求原始站点。
     * 如果配置中的源为"*"，则从请求头中获取实际的来源；否则返回配置中指定的源。
     *
     * @param request HttpServletRequest对象，用于获取请求头中的"Origin"信息。
     * @return 解析得到的请求头值，即允许的源。
     */
    private String resolveOrigin(HttpServletRequest request) {
        return allowedOrigins.equals("*")? request.getHeader("Origin") : allowedOrigins;
    }
}